--
-- CSSE2000 8 Bit Microprocessor
-- Copyright (C) 2011 Nathan Rossi (University of Queensland)
--
-- THIS DESIGN/CODE IS PROVIDED TO YOU UNDER THE FOLLOWING LICENSE:
--
-- All material is restricted to use in the CSSE2000 Project for 2011.
-- You may not redistribute the file/code/design, without the consent of the author.
--

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

library work;
use work.proc_package.ALL;

entity proc_controlunit is
	port (
		clk : in std_logic;
		rst : in std_logic;
		en : in std_logic;
		
		-- Instruction
		instruction : in PROC_PROG_DATA_TYPE;
		
		-- Immediate Bus Output
		immediate_value : out PROC_REG_DATA_TYPE;
		
		-- Register File Control
		register_en : out std_logic;
		register_p_0_write : out std_logic;
		register_p_0_write_source : out PROC_MUX_SOURCE;
		register_p_0_addr_direct : out PROC_REG_ADDR_TYPE;
		register_p_1_addr_direct : out PROC_REG_ADDR_TYPE;
		
		-- ALU Control
		alu_en : out std_logic;
		alu_b_source : out PROC_MUX_SOURCE;
		alu_mode : out PROC_ALU_MODE;
		
		-- Program Counter Control
		pc_en : out std_logic;
		pc_jump_source : out PROC_MUX_SOURCE;
		pc_mode : out PROC_PC_MODE;
		
		-- Status Register Control
		sreg_en : out std_logic;
		sreg_source : out PROC_MUX_SOURCE;
		
		-- Status Register Bit Manipulator Control
		sreg_bit_en : out std_logic;
		sreg_bit_index : out PROC_REG_BIT_INDEX_TYPE;
		sreg_bit_value_in : out std_logic;
		sreg_jump_en : out std_logic;
		
		-- Memory Controller Control
		mem_en : out std_logic;
		mem_addr_source : out PROC_MUX_SOURCE
	);
end proc_controlunit;
architecture Behavioral of proc_controlunit is
type stateType is (setup,eat_cycle,eat_2_cycles,reset_machine,en_reg_change_src_mux);
signal currentstate, nextstate: StateType;
signal counter_rst, counter_mode, counter_in, counter_out: std_logic;
begin
	process(clk,rst,en)	
		begin 
			if rising_edge(clk) then
				if ((rst='1') or (en='0')) then 
					currentstate <=reset_machine;
				else
					currentstate <= nextstate;
				end if;	
			else
				currentstate <= currentstate;
				
			end if;
	end process;
	
	process (Currentstate)
	variable OP_CODE_1, OP_CODE_2, OP_CODE_3, OP_CODE_4: std_logic_vector(3 downto 0);
	variable ALU_OP:PROC_ALU_MODE;
		begin		
				case currentstate is
					when setup =>
						if (en ='1') then
							OP_CODE_1 := instruction(15 downto 12);
							OP_CODE_2 := instruction(11 downto 8);
							OP_CODE_3 := instruction(7 downto 4);
							OP_CODE_4 := instruction(3 downto 0);
					
							if (OP_CODE_1 = "0100") then
								-- LDI, CLR, SER ------	
								register_p_0_addr_direct <= OP_CODE_4;
								immediate_value <= OP_CODE_2 & OP_CODE_3;
								register_en <='1';
								register_p_0_write <='1';
								register_p_0_write_source <=SRC_IMMEDIATE;
								nextstate <= eat_2_cycles;
						
							elsif (OP_CODE_1 = "0000") then
								if (OP_CODE_2 = "0100") then
									-----MOV------
									--setup Registers
									register_en <='1';
									register_p_1_addr_direct <= OP_CODE_3;
									register_p_0_addr_direct <= OP_CODE_4;
									register_p_0_write <='1';
									--setup the mux
									register_p_0_write_source <= SRC_REGISTER_P_1;
									nextstate <= eat_2_cycles;
									if ((OP_CODE_2 = "0101") or (OP_CODE_2 = "0110") or 
										(OP_CODE_2 = "0111") or (OP_CODE_2 = "1000") or  (OP_CODE_2 = "1010"))then
										if OP_CODE_2 = "0101" then
											ALU_OP:=ALU_ADD_MODE;
										elsif OP_CODE_2 = "0110" then
											ALU_OP:=ALU_OR_MODE;
										elsif OP_CODE_2 = "0111" then
											ALU_OP:=ALU_XOR_MODE;
										elsif OP_CODE_2 = "1000" then
											ALU_OP:=ALU_ADD_MODE;
										elsif OP_CODE_2 = "1010" then
											ALU_OP:=ALU_SUB_MODE;					
										end if;
										--register_en : out std_logic;
										register_en <='1';
										register_p_0_write_source <= SRC_ALU_RESULT;
										register_p_0_addr_direct <= OP_CODE_3;
										register_p_1_addr_direct <= OP_CODE_4;
										-- ALU Control
										alu_en <='1';
										alu_b_source <=SRC_REGISTER_P_1;
										alu_mode <=ALU_OP;
										-- Status Register Control
										sreg_en <='1';
										sreg_source <=SRC_ALU_STATUS;

									nextstate <= en_reg_change_src_mux;
									end if;	
											
									
								elsif (OP_CODE_2 = "0000") then
									if (OP_CODE_3 = "0010") then
										--SSR and CSR--
											sreg_bit_value_in <=OP_CODE_4(3);
											sreg_bit_index <=OP_CODE_4(2 downto 0);
											sreg_bit_en<='1';
											sreg_en<='1';
											sreg_source <=SRC_SREG_BITSET;
									
											nextstate <= eat_2_cycles;
									elsif (OP_CODE_3 = "0000") then
										if (OP_CODE_4 = "0000") then -- nop
											---NOP----
											nextstate <= eat_2_cycles;
										end if;
									end if;
								else
									nextstate<=reset_machine;
								end if;
							end if;
						else
								nextstate<=reset_machine;
						end if;			
					when eat_2_cycles =>
						nextstate <= eat_cycle;
				
					when eat_cycle =>
						nextstate <= reset_machine;

					when reset_machine =>	

						-- Immediate Bus Output
						immediate_value <=x"00";
						-- Register File Control
						register_en <='0';
						register_p_0_write <='0';
						register_p_0_write_source <=SRC_IMMEDIATE;
						register_p_0_addr_direct <=x"0";
						register_p_1_addr_direct <=x"0";
						-- ALU Control
						alu_en <='0';
						alu_b_source <=SRC_IMMEDIATE;
						alu_mode <=NONE;
						-- Program Counter Control
						pc_en <='0';
						pc_jump_source <=SRC_IMMEDIATE;
						pc_mode <= PC_NORMAL;
						-- Status Register Control
						sreg_en <='0';
						sreg_source <= SRC_ALU_STATUS;
						-- Status Register Bit Manipulator Control
						sreg_bit_en <='0';
						sreg_bit_index <= "000";
						sreg_bit_value_in <='0';
						sreg_jump_en <='0';
						-- Memory Controller Control
						mem_en <='0';
						mem_addr_source <=SRC_IMMEDIATE;
					
						nextstate <= setup;
						
					when en_reg_change_src_mux =>
						register_p_0_write <='1';
						nextstate <=eat_cycle;
				end case;
		end process;
end Behavioral;